﻿using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using Hardcodet.Wpf.TaskbarNotification;

namespace Songhay.Wpf.UriTree.Managers
{
    using Models;
    using Properties;
    using Songhay.Data.Linq.Opml;
    using Songhay.Wpf.Mvvm.View;
    using Songhay.Xml;

    /// <summary>
    /// Interaction logic for <c>Hardcodet.Wpf.TaskbarNotification</c> visuals.
    /// </summary>
    public partial class TaskbarIconManager : IDisposable
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TaskbarIconManager"/> class.
        /// </summary>
        /// <param name="opmlMetaData">The opml meta-data.</param>
        public TaskbarIconManager(IOpmlMetaData opmlMetaData)
        {
            OpmlMetaData = opmlMetaData;
        }

        /// <summary>
        /// Builds the menu from local OPML.
        /// </summary>
        /// <param name="opml">The opml.</param>
        public void BuildMenuFromLocal(IOpmlSet opml)
        {
            if(opml == null || !opml.IsFileLoaded) return;

            var namePrefix = "LocalItem";
            this.BuildMenu(opml, namePrefix, _separatorDefault);
        }

        /// <summary>
        /// Builds the menu from remote OPML.
        /// </summary>
        /// <param name="opml">The opml.</param>
        public void BuildMenuFromRemote(IOpmlSet opml)
        {
            if(opml == null || !opml.IsFileLoaded) return;

            var namePrefix = "RemoteItem";
            this.BuildMenu(opml, namePrefix, _separatorOpmlLocal);
        }

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated
        /// with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if(this.Tray != null) this.Tray.Dispose();
        }

        #endregion

        /// <summary>
        /// Loads the OPML.
        /// </summary>
        /// <remarks>
        /// The Client Container calls this method when the Application Settings are loaded.
        /// </remarks>
        public void LoadOpml()
        {
            this.BuildMenu();

            this.ShowBalloonMessage("Loading local OPML...", BalloonIcon.Info);
            var uri = new Uri(Settings.Default.ResourcesLocal, UriKind.RelativeOrAbsolute);
            if(!uri.IsAbsoluteUri)
            {
                var root = FrameworkAssembly.GetAssemblyPath(Assembly.GetExecutingAssembly());
                var path = uri.OriginalString;
                path = FrameworkFile.JoinPathAndRoot(path, root);
                uri = new Uri(path, UriKind.Absolute);
            }

            OpmlMetaData.ResourceLocation = uri;
            this.OpmlSetFromLocal = new OpmlSet(OpmlMetaData);
            this.OpmlSetFromLocal.OpmlFileLoaded += (s, args) =>
            {
                this.BuildMenuFromLocal(this.OpmlSetFromLocal);
            };

            this.ShowBalloonMessage("Loading remote OPML...", BalloonIcon.Info);
            OpmlMetaData.ResourceLocation = new Uri(Settings.Default.ResourcesRemote, UriKind.Absolute);
            this.OpmlSetFromRemote = new OpmlSet(OpmlMetaData);
            this.OpmlSetFromRemote.OpmlFileLoaded += (s, args) =>
            {
                this.HideBallonMessage();
                this.BuildMenuFromRemote(this.OpmlSetFromRemote);
            };
        }

        /// <summary>
        /// Gets the opml set from local source.
        /// </summary>
        /// <value>The opml set from local.</value>
        public OpmlSet OpmlSetFromLocal { get; set; }

        /// <summary>
        /// Gets the opml set from remote source.
        /// </summary>
        /// <value>The opml set from remote.</value>
        public OpmlSet OpmlSetFromRemote { get; set; }

        /// <summary>
        /// Records the last loaded OPML node.
        /// </summary>
        /// <param name="itemText">The item text.</param>
        public void RecordLastLoadedNode(string itemText)
        {
            OpmlMetaData.LastLoadedNode = string.Format("Last loaded node: “{0}”", itemText);
        }

        /// <summary>
        /// Gets or sets the tray.
        /// </summary>
        /// <value>The tray.</value>
        public TaskbarIcon Tray { get; private set; }

        internal static IOpmlMetaData OpmlMetaData { get; private set; }

        void BuildMenu()
        {
            if(Tray != null) return;

            Tray = new TaskbarIcon();
            Tray.Icon = Songhay.Wpf.UriTree.Properties.Resources.Icon;
            Tray.ContextMenu = new ContextMenu();

            _separatorOpmlLocal = new Separator { Name = "OpmlLocalSeparator" };
            Tray.ContextMenu.Items.Add(_separatorOpmlLocal);

            _separatorDefault = new Separator { Name = "DefaultSeparator" };
            Tray.ContextMenu.Items.Add(_separatorDefault);

            #region Use Alternative Browser:

            var item = new MenuItem { Header = "Use Alternative Browser" };
            item.IsChecked = ClientContainer.UseAlternateBrowser;
            item.Click += (s, args) =>
            {
                if(!(s is MenuItem)) return;
                var m = s as MenuItem;
                ClientContainer.UseAlternateBrowser = !ClientContainer.UseAlternateBrowser;
                m.IsChecked = ClientContainer.UseAlternateBrowser;

                if(System.IO.File.Exists(ClientContainer.AlternateBrowser)) return;
                m.IsChecked = false;
                string msg = string.Format("The alternative browser “{0}” cannot be found.", ClientContainer.AlternateBrowser);
                this.ShowBalloonMessage(msg, BalloonIcon.Error);
            };
            Tray.ContextMenu.Items.Add(item);

            Tray.ContextMenu.Items.Add(new Separator());

            #endregion

            #region Refresh Data:

            item = new MenuItem { Header = "Refresh Data" };
            item.Click += (s, args) => this.LoadOpml();
            item.Icon = GetImage(TaskBarIconName.Xml);
            Tray.ContextMenu.Items.Add(item);

            #endregion

            #region About:

            item = new MenuItem { Header = "_About…" };
            item.Click += (s, args) =>
            {
                var about = new AboutView();
                about.ShowDialog();
            };
            item.Icon = GetImage(TaskBarIconName.Logo, new Size(16, 16));
            Tray.ContextMenu.Items.Add(item);

            Tray.ContextMenu.Items.Add(new Separator());

            #endregion

            #region Exit:

            item = new MenuItem { Header = "E_xit" };
            item.Click += (s, args) => ClientContainer.ShutdownApplication();
            item.Icon = GetImage(TaskBarIconName.Exit);
            Tray.ContextMenu.Items.Add(item);

            #endregion

        }

        void BuildMenu(IOpmlSet opml, string namePrefix, Separator bottomSeparator)
        {
            if(opml == null) return;

            try
            {
                var document = XObjectUtility.GetXElement(opml.Root, Settings.Default.XpathOpmlBody);
                var categories = XObjectUtility.GetXElements(document, Settings.Default.XpathUriCategories);

                var items = Tray.ContextMenu.Items;
                if(items == null) return;

                items.OfType<MenuItem>().Where(m => m.Name.StartsWith(namePrefix))
                    .ToList().ForEach(m => items.RemoveAt(items.IndexOf(m)));

                var counter = 0;
                categories.ToList().ForEach(c =>
                {
                    var displayName = XObjectUtility.GetValue(c, Settings.Default.XpathUriName);
                    if(c.HasElements)
                    {
                        var item = new MenuItem
                        {
                            Header = displayName,
                            Name = string.Format("{0}{1}", namePrefix, counter)
                        };
                        item.Icon = this.GetImage(TaskBarIconName.Folder);
                        this.LoadChildItems(item, c, opml, namePrefix);
                        items.Insert(items.IndexOf(bottomSeparator), item);
                        this.RecordLastLoadedNode(displayName);
                    }
                    counter++;
                });
            }
            catch(Exception ex)
            {
                this.ShowBalloonMessage(String.Concat(ex.Message, Environment.NewLine, OpmlMetaData.LastLoadedNode), BalloonIcon.Error);
            }
        }

        System.Drawing.Icon GetIcon(TaskBarIconName iconName)
        {
            var iconUri = GetImageUri(iconName);
            var streamInfo = ClientContainer.GetResourceStream(iconUri);
            var icon = new System.Drawing.Icon(streamInfo.Stream);
            return icon;
        }

        Image GetImage(TaskBarIconName iconName)
        {
            return this.GetImage(iconName, Size.Empty);
        }

        Image GetImage(TaskBarIconName iconName, Size imageSize)
        {
            var iconUri = GetImageUri(iconName);
            var image = (imageSize.Equals(Size.Empty))
            ? new Image { Source = new BitmapImage(iconUri) }
            : new Image { Source = new BitmapImage(iconUri), Width = imageSize.Width, Height = imageSize.Height };
            return image;
        }

        Uri GetImageUri(TaskBarIconName iconName)
        {
            var uri = default(Uri);

            switch(iconName)
            {
                case TaskBarIconName.Error:
                    uri = new Uri("pack://application:,,,/Bitmaps/Error.ico", UriKind.Absolute);
                    break;
                case TaskBarIconName.Exit:
                    uri = new Uri("pack://application:,,,/Bitmaps/Exit.ico", UriKind.Absolute);
                    break;
                case TaskBarIconName.Folder:
                    uri = new Uri("pack://application:,,,/Bitmaps/Folder.ico", UriKind.Absolute);
                    break;
                case TaskBarIconName.Info:
                    uri = new Uri("pack://application:,,,/Bitmaps/Info.ico", UriKind.Absolute);
                    break;
                case TaskBarIconName.Logo:
                    uri = new Uri("pack://application:,,,/Bitmaps/Logo.ico", UriKind.Absolute);
                    break;
                case TaskBarIconName.Node:
                    uri = new Uri("pack://application:,,,/Bitmaps/Node.ico", UriKind.Absolute);
                    break;
                case TaskBarIconName.Warning:
                    uri = new Uri("pack://application:,,,/Bitmaps/Warning.ico", UriKind.Absolute);
                    break;
                case TaskBarIconName.Xml:
                    uri = new Uri("pack://application:,,,/Bitmaps/Xml.ico", UriKind.Absolute);
                    break;
            }

            return uri;
        }

        void HideBallonMessage()
        {
            Tray.HideBalloonTip();
        }

        void ShowBalloonMessage(string message, BalloonIcon iconName)
        {
            Tray.ShowBalloonTip(ClientContainer.ApplicationTitle, message, iconName);
        }

        Separator _separatorDefault;
        Separator _separatorOpmlLocal;

    }
}
